import { ApiResponse } from "apisauce"
import { applySnapshot, flow, Instance, SnapshotIn, SnapshotOut, types } from "mobx-state-tree"

import {
  getModels as apiGetModels,
  type AgentModel as AgentDescriptor,
  type GetModelsReq,
  type GetModelsResp,
} from "@/services/api/agents"

const createInitialSnapshot = () => ({
  models: [] as AgentDescriptor[],
  isLoading: false,
  lastFetchedAt: null as number | null,
  errorMessage: null as string | null,
})

export const AgentsModel = types
  .model("Agents", {
    models: types.optional(types.array(types.frozen<AgentDescriptor>()), []),
    lastFetchedAt: types.maybeNull(types.number),
    errorMessage: types.maybeNull(types.string),
  })
  .volatile(() => ({
    isLoading: false,
  }))
  .views((self) => ({
    get hasData() {
      return self.models.length > 0
    },
    get modelsByName() {
      return new Map(self.models.map((agent) => [agent.model, agent]))
    },
  }))
  .actions((self) => {
    function reset() {
      applySnapshot(self, createInitialSnapshot())
    }

    function setModels(models: AgentDescriptor[]) {
      self.models.replace(models)
      self.lastFetchedAt = Date.now()
    }

    function upsertModel(agent: AgentDescriptor) {
      const index = self.models.findIndex((item) => item.model === agent.model)
      if (index >= 0) {
        self.models.splice(index, 1, agent)
      } else {
        self.models.push(agent)
      }
      self.lastFetchedAt = Date.now()
    }

    const fetchModels = flow(function* fetchModels(
      params?: GetModelsReq,
      options?: { force?: boolean },
    ) {
      console.log("self.isLoading", self.isLoading)
      if (self.isLoading) {
        return { success: false, error: "loading" as const }
      }

      self.isLoading = true
      self.errorMessage = null
      console.log("start fetchModels", { params, options })
      try {
        const response = (yield apiGetModels(params)) as ApiResponse<GetModelsResp>
        if (response.ok && response.data?.code === 200) {
          const { models } = response.data.data
          setModels(models)
          return { success: true, data: models }
        }

        const error = response.data ?? response.problem ?? "UnknownError"
        self.errorMessage = typeof error === "string" ? error : JSON.stringify(error)
        return { success: false, error }
      } catch (error) {
        const message = error instanceof Error ? error.message : String(error)
        self.errorMessage = message
        return { success: false, error: message }
      } finally {
        self.isLoading = false
      }
    })

    return {
      reset,
      setModels,
      upsertModel,
      fetchModels,
    }
  })

export interface Agents extends Instance<typeof AgentsModel> {}
export interface AgentsSnapshotOut extends SnapshotOut<typeof AgentsModel> {}
export interface AgentsSnapshotIn extends SnapshotIn<typeof AgentsModel> {}

export const createAgentsDefaultModel = () =>
  types.optional(AgentsModel, () => createInitialSnapshot())

export const createAgentsInitialSnapshot = () => createInitialSnapshot()
